package com.abapi.pay.admin.support.db.service.impl;

import com.abapi.cloud.common.protocol.Result;
import com.abapi.cloud.common.utils.JsonUtil;
import com.abapi.pay.admin.data.ClientData;
import com.abapi.pay.admin.data.ClientInfo;
import com.abapi.pay.admin.support.db.domain.PayBackInfo;
import com.abapi.pay.admin.support.db.dto.request.BackInfoQueryDTO;
import com.abapi.pay.admin.support.db.dto.response.TableResult;
import com.abapi.pay.admin.support.db.jpa.PayBackInfoRepository;
import com.abapi.pay.admin.support.db.service.PayBackInfoService;
import com.abapi.pay.admin.utils.XMLUtil;
import com.abapi.pay.core.biz.Encode;
import com.abapi.pay.core.constans.MessageType;
import com.abapi.pay.core.dto.AliBizDTO;
import com.abapi.pay.core.dto.MessageDTO;
import com.abapi.pay.core.dto.SuccessDTO;
import com.abapi.pay.core.dto.WxBizDTO;
import com.abapi.pay.core.enums.PayType;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import io.netty.buffer.ByteBuf;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.xml.sax.InputSource;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author ldx
 * @Date 2019/11/4 10:45
 * @Description
 * @Version 1.0.0
 */
@Service
public class PayBackInfoServiceImpl implements PayBackInfoService {

    Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    PayBackInfoRepository payBackInfoRepository;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result handle(String projectName, String payType, String payMethod, HttpServletRequest request, HttpServletResponse response) {
        Map<String, String> params = null;
        if(payType.equals(PayType.ali.toString())){
            params = this.aliBackParam(request);
        }else if(payType.equals(PayType.wx.toString())){
            params = this.wxBackParam(request);
        }
        String outTradeNo = params.get("out_trade_no");
        PayBackInfo payBackInfo = payBackInfoRepository.findByOutTradeNoAndProjectName(outTradeNo, projectName);
        if(payBackInfo == null){
            payBackInfo = new PayBackInfo();
            payBackInfo.setNoticeStatus(0);
            payBackInfo.setOutTradeNo(outTradeNo);
            payBackInfo.setProjectName(projectName);
            payBackInfo.setPayType(payType);
            payBackInfo.setPayMethod(payMethod);
            payBackInfo.setCreateTime(new Date());
            payBackInfo.setBackInfo(JSON.toJSONString(params));
            payBackInfoRepository.save(payBackInfo);
        }

        //TODO 负载均衡 轮询(RoundRobin) 推送 客户端
        List<ClientInfo> clientInfos = ClientData.clientData.get(projectName);
        ClientInfo clientInfo = clientInfos.stream().findFirst().get();

        MessageDTO messageDTO = new MessageDTO();
        if(payType.equals(PayType.ali.toString())){
            messageDTO.setType(MessageType.ALI_BIZ);
            AliBizDTO dto = new AliBizDTO();
            dto.setId(payBackInfo.getId());
            dto.setBackInfo(payBackInfo.getBackInfo());
            dto.setClassName(clientInfo.getClassName());
            dto.setMethod(payMethod+"_"+payType);
            messageDTO.setBody(dto);
        }else if(payType.equals(PayType.wx.toString())){
            messageDTO.setType(MessageType.WX_BIZ);
            WxBizDTO dto = new WxBizDTO();
            dto.setId(payBackInfo.getId());
            dto.setBackInfo(payBackInfo.getBackInfo());
            dto.setClassName(clientInfo.getClassName());
            dto.setMethod(payMethod+"_"+payType);
            messageDTO.setBody(dto);
        }

        ByteBuf resultBuf = clientInfo.getTcpSession().alloc().buffer();
        resultBuf.writeBytes(Encode.encode(messageDTO));
        clientInfo.getTcpSession().channel().writeAndFlush(resultBuf);

        return Result.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void success(SuccessDTO dto) {
        Optional<PayBackInfo> optional = payBackInfoRepository.findById(dto.getId());
        if(optional.isPresent()){
            PayBackInfo payBackInfo = optional.get();
            payBackInfo.setNoticeStatus(1);
            payBackInfoRepository.save(payBackInfo);
        }

    }

    @Override
    public Result query(BackInfoQueryDTO queryDTO, HttpServletRequest request) {
        Pageable pageable = PageRequest.of(queryDTO.getPage()-1,queryDTO.getLimit());

        Specification<PayBackInfo> spec = new Specification<PayBackInfo>() {
            private static final long serialVersionUID = -594262632507712037L;
            @Override
            public Predicate toPredicate(Root<PayBackInfo> root, CriteriaQuery<?> crite, CriteriaBuilder cb) {
                List<Predicate> pr = new ArrayList<>();
                /*if( user!= null && user.getName() != null){
                    pr.add(cb.like(root.get("name").as(String.class), "%"+user.getName()+"%"));
                }*/
                return cb.and(pr.toArray(new Predicate[pr.size()]));
            }
        };
        Page<PayBackInfo> page = payBackInfoRepository.findAll(spec, pageable);
        List<PayBackInfo> content = page.getContent();
        System.out.println(JsonUtil.writeValueAsString(content));
        Map<String,Object> map = Maps.newHashMap();
        map.put("count",page.getTotalElements());
        map.put("item",content);
        return TableResult.success("0","success",content, page.getTotalElements());
    }


    private Map<String,String> aliBackParam(HttpServletRequest request){
        //获取支付宝POST过来反馈信息
        Map<String,String> params = new HashMap<String,String>();
        Map requestParams = request.getParameterMap();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            //乱码解决，这段代码在出现乱码时使用。
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }
        //切记alipaypublickey是支付宝的公钥，请去open.alipay.com对应应用下查看。
        logger.info("支付宝 回调参数>>>>"+ JSON.toJSONString(params));
       return params;
    }

    /**
     * 获取回调参数
     * @param request
     * @return
     */
    public Map<String, String> wxBackParam(HttpServletRequest request){
        Document doc = null;
        try {
            InputStream inStream = request.getInputStream();
            ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = inStream.read(buffer)) != -1) {
                outSteam.write(buffer, 0, len);
            }
            outSteam.close();
            inStream.close();
            String resultStr  = new String(outSteam.toByteArray(),"utf-8");

            StringReader read = new StringReader(resultStr);
            InputSource inputSource = new InputSource(read);
            SAXReader sb = new SAXReader();
            doc = sb.read(inputSource);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        Map<String, String> map = XMLUtil.Dom2Map(doc);
        logger.info("支付宝 回调参数>>>>"+ JSON.toJSONString(map));
        return map;
    }

}
